package com.zzyl.nursing.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.*;
import com.zzyl.nursing.dto.*;
import com.zzyl.nursing.mapper.BedMapper;
import com.zzyl.nursing.service.ICheckInConfigService;
import com.zzyl.nursing.service.IContractService;
import com.zzyl.nursing.service.IElderService;
import com.zzyl.nursing.utils.CodeGenerator;
import com.zzyl.nursing.vo.CheckInConfigVo;
import com.zzyl.nursing.vo.CheckInDetailVo;
import com.zzyl.nursing.vo.CheckInElderVo;
import com.zzyl.nursing.vo.ElderFamilyVo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.CheckInMapper;
import com.zzyl.nursing.service.ICheckInService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;

/**
 * 入住Service业务层处理
 *
 * @author ruoyi
 * @date 2024-12-26
 */
@Service
@Transactional
@RequiredArgsConstructor
public class CheckInServiceImpl extends ServiceImpl<CheckInMapper, CheckIn> implements ICheckInService {

    public static final int BED_USED = 1;
    public static final int ELDER_USED = 0;

    private final CheckInMapper checkInMapper;
    // 老人的服务
    private final IElderService elderService;
    // 床
    private final BedMapper bedMapper;
    // 入住配置
    private final ICheckInConfigService checkInConfigService;
    // 入住合同
    private final IContractService contractService;

    /**
     * 根据入住记录ID查询入住详情，组装老人信息、家属信息、入住配置和合同信息并返回。
     *
     * 设计要点：
     * 1) 以 checkInId 为入口，先查入住主表，再根据其中的 elderId 去查老人、合同等。
     * 2) remark 字段存的是家属列表的 JSON 字符串，这里需要反序列化为 List<ElderFamilyVo>。
     * 3) 年龄未冗余存储，运行时根据生日计算（当前实现为简单的“年份差”）。
     *
     * @param id 入住记录ID（checkIn.id）
     * @return 组装好的入住详情VO
     * @throws BaseException 当找不到入住信息时抛出
     */
    @Override
    public CheckInDetailVo detail(Long id) {
        // 统一的返回载体VO
        CheckInDetailVo checkInDetailVo = new CheckInDetailVo();

        // === 1. 读取入住信息（主表） ===
        // 以传入的 checkInId 直接查询；若查不到则中断流程，抛业务异常。
        CheckIn checkIn = checkInMapper.selectById(id);
        if (checkIn == null) {
            throw new BaseException("找不到入住信息");
        }

        // === 2. 获取老人基本信息 ===
        // 从入住信息里拿到 elderId，再去老人服务查询完整信息。
        Long elderId = checkIn.getElderId();
        Elder elder = elderService.selectElderById(elderId);

        // 组装老人VO（CheckInElderVo）并拷贝同名属性
        CheckInElderVo checkInElderVo = new CheckInElderVo();
        BeanUtils.copyProperties(elder, checkInElderVo);

        // 年龄没有冗余存储，这里运行时计算。
        // 说明：当前写法仅用“年份差”，对还未到生日的情况会多算1岁。
        // 如果需要更精确年龄，建议使用 Period.between(...)（见下方注释备选）。
        // 假设 elder.getBirthday() 为 "yyyy-MM-dd" 格式；若格式不确定，建议使用 DateTimeFormatter 明确解析。
        LocalDate birthDay = LocalDate.parse(elder.getBirthday());
        checkInElderVo.setAge(LocalDateTime.now().getYear() - birthDay.getYear());
        // 更准确的写法（备选）：
        // checkInElderVo.setAge(Period.between(birthDay, LocalDate.now()).getYears());

        // 老人VO放入详情
        checkInDetailVo.setCheckInElderVo(checkInElderVo);

        // === 3. 家属信息（remark 中存 JSON 数组） ===
        // remark 可能为空或空串；为空时前端若直接渲染可能NPE，这里统一返回空集合以便前端安全处理。
        if (StringUtils.hasText(checkIn.getRemark())) {
            // 约定：remark 存的是 ElderFamilyVo 的 JSON 数组字符串
            List<ElderFamilyVo> elderFamilyVoList = JSON.parseArray(checkIn.getRemark(), ElderFamilyVo.class);
            checkInDetailVo.setElderFamilyVoList(elderFamilyVoList);
        } else {
            checkInDetailVo.setElderFamilyVoList(new ArrayList<>()); // 返回空列表更安全
        }

        // === 4. 入住配置 ===
        // 入住配置与入住信息通过 checkInId 关联，一般一对一，这里用 getOne 精确查一条。
        // 若存在并发/数据异常导致多条，getOne 默认会抛异常；如需容错，可加 .last("limit 1") 或使用 list 再取第一条。
        CheckInConfig checkInConfig = checkInConfigService.getOne(
                Wrappers.<CheckInConfig>lambdaQuery().eq(CheckInConfig::getCheckInId, checkIn.getId())
        );

        CheckInConfigVo checkInConfigVo = new CheckInConfigVo();
        BeanUtils.copyProperties(checkInConfig, checkInConfigVo);

        // 业务上“入住开始/结束时间”在主表 checkIn 里，这里需要手动覆盖到VO，保证前端能正确回显
        checkInConfigVo.setStartDate(checkIn.getStartDate());
        checkInConfigVo.setEndDate(checkIn.getEndDate());

        // 塞到详情VO
        checkInDetailVo.setCheckInConfigVo(checkInConfigVo);

        // === 5. 合同信息 ===
        // 合同按 elderId 关联（一个老人一份/或最近一份合同）。这里直接返回实体 Contract；
        // 若前端期望 VO（如 ContractVo），可与上面 Elder/Config 一致：new ContractVo + copyProperties。
        Contract contract = contractService.getOne(
                Wrappers.<Contract>lambdaQuery().eq(Contract::getElderId, elderId)
        );
        checkInDetailVo.setContract(contract);

        // === 6. 返回聚合好的详情 ===
        return checkInDetailVo;
    }





    @Override
    public void saveCheckIn(CheckInApplyDto checkInApplyDto) {
        // 1. 检查老人的入住状态
        // 查询老人 通过身份证号查询
        LambdaQueryWrapper<Elder> elderQueryWrapper = new LambdaQueryWrapper<>();
        elderQueryWrapper.eq(Elder::getIdCardNo,checkInApplyDto.getCheckInElderDto().getIdCardNo());
        Elder elder = elderService.getOne(elderQueryWrapper);
        if (elder != null && elder.getStatus() == ELDER_USED) {
            throw new RuntimeException("该老人已入住");
        }
        // 2. 插入四张表
        // 判断床位是否被占用
        Bed bed = bedMapper.selectById(checkInApplyDto.getCheckInConfigDto().getBedId());
        if (bed.getBedStatus() != null && bed.getBedStatus() == BED_USED) {
            throw new RuntimeException("该床位已经有人占用");
        }
        // 插入老人信息表
        elder = saveElderInfo(checkInApplyDto, bed);

        // 新增入住信息
        CheckIn checkIn = insertCheckIn(elder, checkInApplyDto);

        // 插入入住配置信息
        saveConfigInfo(checkIn.getId(), checkInApplyDto.getCheckInConfigDto());

        //插入合同信息
        saveContract(checkInApplyDto.getCheckInContractDto(), elder,checkInApplyDto.getCheckInConfigDto());

    }


    private void saveContract(CheckInContractDto checkInContractDto,Elder elder, CheckInConfigDto checkInConfigDto) {
        //生成合同编号
        String contractNo = "HT"+ CodeGenerator.generateContractNumber();
        Contract contract = new Contract();
        BeanUtils.copyProperties(checkInContractDto, contract);
        contract.setElderId(elder.getId());
        contract.setElderName(elder.getName());
        contract.setContractNumber(contractNo);
        //状态、开始时间、结束时间
        //签约时间小于等于当前时间，合同生效中
        LocalDateTime checkInStartTime = checkInConfigDto.getStartDate();
        LocalDateTime checkInEndTime = checkInConfigDto.getEndDate();
        Integer status = checkInStartTime.isAfter(LocalDateTime.now()) ? 1 : 0;
        contract.setStatus(status);
        contract.setStartDate(checkInStartTime);
        contract.setEndDate(checkInEndTime);
        contractService.save(contract);
    }

    /**
     * 新增入住信息
     * @param elder
     * @param checkInApplyDto
     */
    private CheckIn insertCheckIn(Elder elder, CheckInApplyDto checkInApplyDto) {
        CheckIn checkIn = new CheckIn();
        checkIn.setElderName(elder.getName());
        checkIn.setElderId(elder.getId());
        checkIn.setIdCardNo(elder.getIdCardNo());
        checkIn.setStartDate(checkInApplyDto.getCheckInConfigDto().getStartDate());
        checkIn.setEndDate(checkInApplyDto.getCheckInConfigDto().getEndDate());
        checkIn.setNursingLevelName(checkInApplyDto.getCheckInConfigDto().getNursingLevelName());
        checkIn.setBedNumber(elder.getBedNumber());
        //家人信息
        checkIn.setRemark(JSON.toJSONString(checkInApplyDto.getElderFamilyDtoList()));
        checkIn.setStatus(ELDER_USED);
        checkInMapper.insert(checkIn);
        return checkIn;
    }


    private void saveConfigInfo(Long checkId,CheckInConfigDto checkInConfigDto) {
        CheckInConfig config = new CheckInConfig();
        config.setCheckInId(checkId);
        BeanUtils.copyProperties(checkInConfigDto, config);
        checkInConfigService.save(config);
    }

    /**
     * 插入老人信息
     * @param checkInApplyDto
     * @param bed
     * @return
     */
    private Elder saveElderInfo(CheckInApplyDto checkInApplyDto, Bed bed) {
        Elder elder = new Elder();
        elder.setBedId(bed.getId());
        elder.setBedNumber(bed.getBedNumber());
        BeanUtils.copyProperties(checkInApplyDto.getCheckInElderDto(), elder);
        elderService.save(elder);
        return elder;
    }


    /**
     * 查询入住
     *
     * @param id 入住主键
     * @return 入住
     */
    @Override
    public CheckIn selectCheckInById(Long id) {
        return getById(id);
    }

    /**
     * 查询入住列表
     *
     * @param checkIn 入住
     * @return 入住
     */
    @Override
    public List<CheckIn> selectCheckInList(CheckIn checkIn) {
        return checkInMapper.selectCheckInList(checkIn);
    }

    /**
     * 新增入住
     *
     * @param checkIn 入住
     * @return 结果
     */
    @Override
    public int insertCheckIn(CheckIn checkIn) {
        return save(checkIn) ? 1 : 0;
    }

    /**
     * 修改入住
     *
     * @param checkIn 入住
     * @return 结果
     */
    @Override
    public int updateCheckIn(CheckIn checkIn) {
        return updateById(checkIn) ? 1 : 0;
    }

    /**
     * 批量删除入住
     *
     * @param ids 需要删除的入住主键
     * @return 结果
     */
    @Override
    public int deleteCheckInByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除入住信息
     *
     * @param id 入住主键
     * @return 结果
     */
    @Override
    public int deleteCheckInById(Long id) {
        return removeById(id) ? 1 : 0;
    }
}
